package com.qys.livesMall.app.user.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.qys.livesMall.app.user.enums.GoldAccountRecordTypeEnum;
import com.qys.livesMall.app.user.event.GoldRecordEvent;
import com.qys.livesMall.app.user.mapper.H2AppUserMapper;
import com.qys.livesMall.app.user.model.dto.GoldChangeDTO;
import com.qys.livesMall.app.user.model.dto.sys.AppUserGoldAccountDTO;
import com.qys.livesMall.app.user.model.dto.sys.GoldRecordEventDTO;
import com.qys.livesMall.app.user.model.entity.AppUserGoldAccount;
import com.qys.livesMall.app.user.mapper.AppUserGoldAccountMapper;
import com.qys.livesMall.app.user.model.entity.AppUserGoldRecord;
import com.qys.livesMall.app.user.model.entity.H2AppUser;
import com.qys.livesMall.app.user.service.AppUserGoldAccountService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qys.livesMall.app.user.service.AppUserGoldRecordService;
import com.qys.livesMall.cache.utils.CacheUtils;
import com.qys.livesMall.common.exception.QysException;
import com.qys.livesMall.common.utils.MessageUtils;
import com.qys.livesMall.finance.enums.AccountBusinessTypeEnum;
import com.qys.livesMall.finance.model.dto.FinancePayDTO;
import com.qys.livesMall.finance.service.FinanceBusinessService;
import com.qys.livesMall.finance.util.BusinessNoUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 用户金币账户表 服务实现类
 * </p>
 *
 * @author wzy
 * @since 2023-06-24
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class AppUserGoldAccountServiceImpl extends ServiceImpl<AppUserGoldAccountMapper, AppUserGoldAccount> implements AppUserGoldAccountService {

    private final CacheUtils cacheUtils;
    private final H2AppUserMapper userMapper;
    private final AppUserGoldRecordService userGoldRecordService;
    private final ApplicationContext applicationContext;
    private final FinanceBusinessService financeBusinessService;

    private static final BigDecimal EXCHANGE_GOLD_RATIO=new BigDecimal("100");


    @Override
    public AppUserGoldAccount getGoldAccountByUserId(Long userId) {
        AppUserGoldAccount goldAccount = getOne(Wrappers.<AppUserGoldAccount>lambdaQuery()
                .eq(AppUserGoldAccount::getUserId, userId)
                .last("limit 1"));

        if (goldAccount == null) {
            String lockName = "userLock:" + userId;
            //获得锁
            boolean isLock = cacheUtils.getLock(lockName, CacheUtils.LOCK_WAITTIME_SECONDS);
            try {
                if (isLock) {
                    //获取锁成功
                    goldAccount = new AppUserGoldAccount();
                    goldAccount.setUserId(userId);
                    goldAccount.setGoldAmount(BigDecimal.ZERO);
                    goldAccount.setUserName(userMapper.getUserNameById(userId));
                    goldAccount.setCreateTime(LocalDateTime.now());
                    baseMapper.insert(goldAccount);
                }
            } finally {
                if (isLock) {
                    //释放锁
                    cacheUtils.releaseLock(lockName);
                }
            }
        }
        return goldAccount;
    }

    @Override
    public BigDecimal exchangeGoldRatio() {
        return EXCHANGE_GOLD_RATIO;
    }

    @Override
    public void addAccounts(List<H2AppUser> users) {
        List<AppUserGoldAccount> accounts=new ArrayList<>();
        users.stream().forEach(user->{
            AppUserGoldAccount goldAccount=new AppUserGoldAccount();
            //获取锁成功
            goldAccount.setUserId(Long.valueOf(user.getId()));
            goldAccount.setGoldAmount(BigDecimal.ZERO);
            goldAccount.setUserName(user.getUserName());
            goldAccount.setCreateTime(LocalDateTime.now());

            accounts.add(goldAccount);
        });

        this.saveBatch(accounts);
    }

    /**
     * 赠送直播礼物
     *
     * @param userId
     * @param goldAmount
     */
    @Override
    public boolean giveLivesGift(Long userId, BigDecimal goldAmount) {
        return reduceBalanceSync(userId, goldAmount, GoldAccountRecordTypeEnum.GIVE_LIVES_GIFT);
    }

    /**
     * 余额兑换金币
     *
     * @param userId
     * @param goldAmount
     */
    @Override
    public boolean balanceExchangeGold(Long userId, BigDecimal goldAmount) {
        return addBalanceSync(userId, goldAmount, GoldAccountRecordTypeEnum.BALANCE_EXCHANGE_GOLD);
    }

    @Override
    public boolean editBatch(AppUserGoldAccountDTO dto) {
        for (Long userId:dto.getUserIds()) {
            try {
                addBalanceSync(userId, dto.getGoldAmount(), GoldAccountRecordTypeEnum.BALANCE_EXCHANGE_GOLD);
            } catch (Exception e) {
                log.error("用户账户余额不足兑换代币失败 userId："+userId);
            }
        }
        return true;
    }

    /***操作增加金币*/
    @Transactional(rollbackFor = Exception.class)
    public boolean addBalanceSync(Long userId, BigDecimal goldAmount, GoldAccountRecordTypeEnum recordTypeEnum) {
        if (goldAmount.compareTo(BigDecimal.ZERO) == 0) {
            return true;
        }
        if (goldAmount.compareTo(BigDecimal.ZERO) < 0) {
            throw new QysException("操作金币必须大于0");
        }
        GoldChangeDTO goldChangeDTO=new GoldChangeDTO();
        goldChangeDTO.setUserId(userId).setIncomeType((byte)1);
        goldChangeDTO.setExchangeGoldQuantity(goldAmount);
        goldChangeDTO.setRecordTypeEnum(recordTypeEnum);
        return changeGoldAccount(goldChangeDTO);
    }

    /***操作减少金币*/
    private boolean reduceBalanceSync(Long userId, BigDecimal goldAmount, GoldAccountRecordTypeEnum recordTypeEnum) {
        if (goldAmount.compareTo(BigDecimal.ZERO) == 0) {
            return true;
        }
        if (goldAmount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new QysException("操作金币必须小于0");
        }
        GoldChangeDTO goldChangeDTO=new GoldChangeDTO();
        goldChangeDTO.setUserId(userId).setIncomeType((byte)2);
        goldChangeDTO.setExchangeGoldQuantity(goldAmount);
        goldChangeDTO.setRecordTypeEnum(recordTypeEnum);
        return changeGoldAccount(goldChangeDTO);
    }

    private boolean changeGoldAccount(GoldChangeDTO goldChangeDTO) {
        BigDecimal exchangeGoldQuantity = goldChangeDTO.getExchangeGoldQuantity();
        //兑换比例  1:100
        BigDecimal changAmount = exchangeGoldQuantity.divide(EXCHANGE_GOLD_RATIO).setScale(4, RoundingMode.FLOOR);

        //当前金币余额
        AppUserGoldAccount goldAccount = getGoldAccountByUserId(goldChangeDTO.getUserId());
        BigDecimal oldGold=goldAccount.getGoldAmount();
        FinancePayDTO payDTO = new FinancePayDTO();
        if(goldChangeDTO.getIncomeType() == (byte)1){
            // 金币购买扣款
            payDTO.setUserId(goldChangeDTO.getUserId());
            payDTO.setBusinessNo(BusinessNoUtil.get(AccountBusinessTypeEnum.LIVE_GOLD_COIN));
            payDTO.setAmountIncurred(changAmount);
            payDTO.setBusinessType(AccountBusinessTypeEnum.LIVE_GOLD_COIN);
            financeBusinessService.pay(payDTO);
            if (!payDTO.getSuccess()){
                throw new QysException(payDTO.getMessage());
            }
            goldAccount.setGoldAmount(goldAccount.getGoldAmount().add(exchangeGoldQuantity));
            goldAccount.setUpdateTime(LocalDateTime.now());
        } else {
            // 赠送金币
            if (goldAccount.getGoldAmount().compareTo(exchangeGoldQuantity) < 0) {
                throw new QysException(MessageUtils.message("金币余额不足"));
            }
            goldAccount.setGoldAmount(goldAccount.getGoldAmount().subtract(exchangeGoldQuantity));
            goldAccount.setUpdateTime(LocalDateTime.now());
        }


        //获得锁
        boolean isLock = cacheUtils.getAccountLock(String.valueOf(goldAccount.getId()), CacheUtils.LOCK_WAITTIME_SECONDS);
        try {
            if (!isLock || baseMapper.updateById(goldAccount) <= 0) {
                throw new QysException(MessageUtils.message("金币账户繁忙"));
            } else {
                AppUserGoldRecord record = new AppUserGoldRecord();
                record.setUserId(goldChangeDTO.getUserId());
                record.setGoldAmount(exchangeGoldQuantity);
                record.setCreateTime(LocalDateTime.now());
                record.setUserName(userMapper.getUserNameById(goldAccount.getUserId()));
                record.setIncomeType(goldChangeDTO.getIncomeType());
                record.setOldGoldAmount(oldGold);
                record.setOrderNo(goldChangeDTO.getIncomeType() == (byte)1 ? payDTO.getBusinessNo() : null);
                record.setRecordType(goldChangeDTO.recordTypeEnum.getType());
                record.setRemark(goldChangeDTO.recordTypeEnum.getRemark(record.getUserName(),record.getGoldAmount(),record.getOldGoldAmount()));
                userGoldRecordService.save(record);
                //用户金币变化监听
                GoldRecordEventDTO goldRecordEventDTO=new GoldRecordEventDTO();
                goldRecordEventDTO.setUserId(record.getUserId());
                goldRecordEventDTO.setGoldAmount(goldAccount.getGoldAmount());
                applicationContext.publishEvent(new GoldRecordEvent(goldRecordEventDTO));
            }
            return true;
        } catch (Exception e) {
            throw e;
        } finally {
            if (isLock) {
                cacheUtils.releaseAccountLock(String.valueOf(goldAccount.getId()));
            }
        }
    }


}
